/*
* Copyright (C) 2019 ~ 2021 Uniontech Software Technology Co.,Ltd.
*
* Author:     yanghongwei <yanghongwei@uniontech.com>
*
* Maintainer: yanghongwei <yanghongwei@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "http_process.h"
#include "person.h"
#include "tuogun_product_device.h"
#include "tuogun_sensor_data.h"
#include "tuogun_device_sensor.h"
#include "tuogun_product_line.h"
#include "common.h"

#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Net/HTTPServerParams.h>
#include <Poco/Net/HTMLForm.h>
#include <Poco/MD5Engine.h>
#include <Poco/DigestStream.h>
#include <Poco/StreamCopier.h>

#include <iostream>
#include <string>
#include <sstream>

#include <plog/Log.h>
#include <nlohmann/json.hpp>

using Poco::DigestEngine;
using Poco::MD5Engine;
using Poco::DigestOutputStream;
using Poco::StreamCopier;

#define API_USER_LOGIN          "/api/client/user_login"
#define API_PRODUCT_LINE        "/api/client/product_line"
#define API_PRODUCT_DEVICE      "/api/client/product_device"
#define API_PRODUCT_SENSOR      "/api/client/product_sensor"
#define API_GET_SENSOR_STATUS   "/api/client/get_sensor_status"
#define API_GET_THRESHOLD       "/api/client/get_threshold"
#define API_SET_THRESHOLD       "/api/client/set_threshold"

enum TuogunSensorReportStatusEnums {
    NORMAL = 1,
    STOP,
    SLOW,
    UNKNOW
};

struct TuogunSensorReportDto{
    uint32_t deviceId;
    std::string deviceName;
    std::string groupCode;
    uint32_t sensorId;
    std::string cellCode;
    uint32_t data;
    TuogunSensorReportStatusEnums status;
};

struct TuogunDeviceGroupDto{
    uint32_t deviceId;
    std::string deviceName;
    int16_t groupCode;
    std::vector<TuogunSensorReportDto> sensorReports;
};

struct TuogunProductInfoDto{
    uint64_t deviceGroupCount;        //网关下传感器组数
    uint64_t deviceMaxGroupCellCount; //网关下面group中设备数量最多的那个组的设备数量
};

std::string md5Encoder(const std::string &data)
{
    MD5Engine md5;
    DigestOutputStream dos(md5);
    std::istringstream istr(data);
    StreamCopier::copyStream(istr, dos);
    dos.close();
    std::string md5_str = DigestEngine::digestToHex(md5.digest());
    return md5_str;
}

bool addSensorReport(TuogunSensorReportDto &report, TuogunProductDevice deviceInfo, TuogunDeviceSensor sensor) {
    if(sensor.group_desc == 0 || sensor.group_no == "") {
        return false;
    }
    TuogunSensorDataManager manager;
    TuogunSensorData data = manager.getLastestTuogunSensorDataBySensorId(sensor.id);
    report.deviceId = deviceInfo.device_id;
    report.deviceName = deviceInfo.device_name;
    report.groupCode = sensor.group_desc;
    report.cellCode = sensor.group_no;
    report.sensorId = sensor.id;

    report.data = data.data;
    int threshold = common::getThreshold();
    if(data.data == 0) {
        report.status = TuogunSensorReportStatusEnums::STOP;
    }else if(data.data > 0 && data.data < threshold) {
        report.status = TuogunSensorReportStatusEnums::SLOW;
    }else if(data.data >= threshold) {
        report.status = TuogunSensorReportStatusEnums::NORMAL;
    }else {
        report.status = TuogunSensorReportStatusEnums::UNKNOW;
    }

    return true;
}

std::vector<TuogunDeviceGroupDto> getDeviceSensorList(uint32_t deviceId)
{
    std::vector<TuogunDeviceGroupDto> result;
    TuogunProductDeviceManager manager;
    TuogunProductDevice deviceInfo = manager.getTuogunProductDeviceByDeviceId(deviceId);
    if(deviceInfo.device_name == "") {
        return result;
    }
    TuogunDeviceSensorManager manager_deviceSensor;
    std::vector<TuogunDeviceSensor> sensors = manager_deviceSensor.getTuogunDeviceSensorByDeviceId(deviceId);
    for(TuogunDeviceSensor sensor : sensors) {
        //组装传感器信息报告对象
        TuogunSensorReportDto report;
        addSensorReport(report, deviceInfo, sensor);
        //如果分组不存在先添加分组再调价传感器信息到分组
        //添加分组到返回结果
        TuogunDeviceGroupDto dto;
        dto.deviceId = deviceId;
        dto.deviceName = deviceInfo.device_name;
        dto.groupCode = sensor.group_desc;
        dto.sensorReports.push_back(report);
        result.push_back(dto);
    }
    
    return result;
}
// John_Yang 统记传感器 普通:停转:缓转:其他
void getStatusTotals(uint32_t product_line_id, int &normalCount, int &stopCount, int &slowCount, int &otherCount)
{
    TuogunProductDeviceManager manager;
    // 获取产线上的所有设备
    std::vector<TuogunProductDevice> devices = manager.getTuogunProductDeviceByProductLine(product_line_id);

    for(TuogunProductDevice device : devices) {
        std::vector<TuogunDeviceGroupDto> reports = getDeviceSensorList(device.device_id);
        for(TuogunDeviceGroupDto group : reports) {
            std::vector<TuogunSensorReportDto> sensors = group.sensorReports;
            for(TuogunSensorReportDto sensor : sensors) {
                if(sensor.status == TuogunSensorReportStatusEnums::NORMAL) {
                    normalCount++;
                }else if(sensor.status == TuogunSensorReportStatusEnums::STOP) {
                    stopCount++;
                }else if(sensor.status == TuogunSensorReportStatusEnums::SLOW) {
                    slowCount++;
                }else {
                    otherCount++;
                }
            }
        }
    }
    LOGD << "normalCount:" << normalCount << ", stopCount:" << stopCount << ", slowCount:" << slowCount << ", otherCount:" << otherCount;
}

std::vector<TuogunSensorData> getDeviceSensorHistoryTenData(uint32_t productLineId, std::string deviceName,
                                                            int16_t group, std::string desc)
{
    TuogunProductDevice productParam;
    productParam.device_name = deviceName;
    productParam.product_line_id = productLineId;
    TuogunProductDeviceManager manager;
    std::vector<TuogunProductDevice> products = TuogunProductDeviceManager::getTuogunProductDeviceBySelective(productLineId, deviceName);
    if(products.size() == 0) {
        return std::vector<TuogunSensorData>();
    }
    LOGD<<"products.size:" << products.size();
    TuogunProductDevice product = products[0];

    std::vector<TuogunDeviceSensor> sensors = TuogunDeviceSensorManager::getTuogunDeviceSensorBySelective(product.device_id, group, desc);
    if(sensors.size() == 0) {
        return std::vector<TuogunSensorData>();
    }
    LOGD<<"sensors.size:" << sensors.size();
    TuogunDeviceSensor sensor = sensors[0];
    std::vector<TuogunSensorData> sensorDatas = TuogunSensorDataManager::getTuogunSensorDataBySensorId(sensor.id);
    std::reverse(sensorDatas.begin(),sensorDatas.end());
    return sensorDatas;
}
// John_Yang 获取传感器的最近10条转速记录
void cmdGetSensorData(uint32_t productLineId, std::string productName, int16_t group, std::string desc)
{
    LOGD<<"productLineId:"<<productLineId <<",productName:"<<productName<<",group:"<<group << ",desc:" << desc;
    std::vector<TuogunSensorData> list = getDeviceSensorHistoryTenData(productLineId, productName, group, desc);
    LOGD<<"list.size():"<<list.size();
    std::vector<int32_t> sbf;
    LOGD << "getSensorHistoryDatas:";
    for(TuogunSensorData data : list) {
        sbf.push_back(data.data);
        LOGD << data.data;
    }
}

// John_Yang 获取产线上某个网关的传感器状态
void cmdGetDeviceSensorInfo(uint32_t productLineId, std::string deviceName)
{
    TuogunProductDevice device = TuogunProductDeviceManager::getTuogunProductDeviceByProductAndName(productLineId, deviceName);
    LOGD << "cmdGetDeviceSensorInfo:";
    std::vector<std::string> sbf;
    std::vector<TuogunDeviceGroupDto> reports = getDeviceSensorList(device.device_id);
    for(TuogunDeviceGroupDto group : reports) {
        std::vector<TuogunSensorReportDto> sensors = group.sensorReports;
        for(TuogunSensorReportDto sensor : sensors) {
            if(sensor.status == TuogunSensorReportStatusEnums::NORMAL) {
                sbf.push_back("1");
                LOGD<<"1";
            }else if(sensor.status == TuogunSensorReportStatusEnums::STOP) {
                sbf.push_back("2");
                LOGD<<"2";
            }else if(sensor.status == TuogunSensorReportStatusEnums::SLOW) {
                sbf.push_back("3");
                LOGD<<"3";
            }else {
                sbf.push_back("0");
                LOGD<<"0";
            }
        }
    }
}

TuogunProductInfoDto getDeviceSensorInfo(uint32_t deviceId) {
    TuogunProductInfoDto result;
    result.deviceGroupCount = TuogunDeviceSensorManager::getGroupDescCountByDeviceId(deviceId);
    result.deviceMaxGroupCellCount = TuogunDeviceSensorManager::getMaxGroupCountByGroupDesc(deviceId);
    return result;
}
std::string cmdGetDeviceInfo(uint32_t deviceId)
{
    TuogunProductInfoDto tuogunProductInfo = getDeviceSensorInfo(deviceId);
    // 如果参数越界，则返回最大值
    tuogunProductInfo.deviceGroupCount = tuogunProductInfo.deviceGroupCount > 99 ? 99 : tuogunProductInfo.deviceGroupCount;
    tuogunProductInfo.deviceMaxGroupCellCount = tuogunProductInfo.deviceMaxGroupCellCount > 9 ? 9 : tuogunProductInfo.deviceMaxGroupCellCount;
    return "";
}

void RootHandler::handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response)
{
    const std::string &method = request.getMethod();
    LOGD << "get request method:" << method;

    if (method.empty()) {
        return;
    }

    if (method == "GET") {
        processRequestGet(request, response);
    } else if (method == "POST") {
        processRequestPost(request, response);
    } else if (method == "PUT") {
        processRequestPut(request, response);
    }
}

void RootHandler::processRequestGet(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response)
{
    std::string uri = request.getURI();
    std::string content_type = request.getContentType();
    uri = uri.substr(0, uri.find('?'));
    content_type = content_type.substr(0, content_type.find(';'));

    std::ostream& ostr = response.send();
    response.setStatus(Poco::Net::HTTPServerResponse::HTTPStatus::HTTP_OK);
    nlohmann::json json;
    if (uri == API_PRODUCT_LINE) {
        Poco::Net::HTMLForm form(request, request.stream());
        int32_t person_id = std::atoi(form.get("person_id", "0").c_str());
        std::vector<TuogunProductLine> vec_productLine = TuogunProductLineManager::getTuogunProductLineByPersId(person_id);
        json.clear();
        nlohmann::json json_array = nlohmann::json::array();
        for (auto product_line : vec_productLine) {
            int normalCount = 0;
            int stopCount = 0;
            int slowCount = 0;
            int otherCount = 0;
            getStatusTotals(product_line.id, normalCount, stopCount, slowCount, otherCount);
            nlohmann::json json_product_line;
            json_product_line["id"] = product_line.id;
            json_product_line["name"] = product_line.name;
            json_product_line["is_enable"] = product_line.is_enable == 1 ? true : false;
            json_product_line["normalCount"] = normalCount;
            json_product_line["stopCount"] = stopCount;
            json_product_line["slowCount"] = slowCount;
            json_product_line["otherCount"] = otherCount;
            json_array.push_back(json_product_line);
        }
        json["code"] = 0;
        json["product_line_list"] = json_array;
        json["message"] = "successful";
        LOGD << "processRequestGet:" << json.dump();
    } else if (uri == API_PRODUCT_DEVICE) {
        Poco::Net::HTMLForm form(request, request.stream());
        uint32_t product_line_id = std::atoi(form.get("product_line_id", "0").c_str());
        std::vector<TuogunProductDevice> vec_productDevice = TuogunProductDeviceManager::getTuogunProductDeviceByProductLine(product_line_id);
        json.clear();
        nlohmann::json json_array = nlohmann::json::array();
        for (auto product_device : vec_productDevice) {
            nlohmann::json json_product_device;
            json_product_device["id"] = product_device.device_id;
            json_product_device["name"] = product_device.device_name;
            json_product_device["is_enable"] = product_device.is_enable == 1 ? true : false;
            json_array.push_back(json_product_device);
        }
        json["code"] = 0;
        json["product_device_list"] = json_array;
        json["message"] = "successful";
        LOGD << "processRequestGet:" << json.dump();
    } else if (uri == API_PRODUCT_SENSOR) {
        Poco::Net::HTMLForm form(request, request.stream());
        uint32_t product_device_id = std::atoi(form.get("product_device_id", "0").c_str());
        std::vector<TuogunDeviceGroupDto> reports = getDeviceSensorList(product_device_id);
        nlohmann::json json_array_group = nlohmann::json::array();
        for(TuogunDeviceGroupDto group : reports) {
            nlohmann::json json_sensor_group;
            std::vector<TuogunSensorReportDto> sensors = group.sensorReports;
            json_sensor_group["group_code"] = group.groupCode;
            nlohmann::json json_array = nlohmann::json::array();
            for(TuogunSensorReportDto sensor : sensors) {
                nlohmann::json json_product_sensor;
                json_product_sensor["id"] = sensor.sensorId;
                json_product_sensor["name"] = sensor.deviceName;
                json_product_sensor["cell_code"] = sensor.cellCode;
                json_product_sensor["data"] = sensor.data;
                if(sensor.status == TuogunSensorReportStatusEnums::NORMAL) {
                    json_product_sensor["status"] = "normal";
                }else if(sensor.status == TuogunSensorReportStatusEnums::STOP) {
                    json_product_sensor["status"] = "stop";
                }else if(sensor.status == TuogunSensorReportStatusEnums::SLOW) {
                    json_product_sensor["status"] = "slow";
                }else {
                    json_product_sensor["status"] = "unknown";
                }
                json_array.push_back(json_product_sensor);
            }
            json_sensor_group["sensor_list"] = json_array;
            json_array_group.push_back(json_sensor_group);
        }
        json["code"] = 0;
        json["product_sensor_group_list"] = json_array_group;
        json["message"] = "successful";
        LOGD << "processRequestGet:" << json.dump();
    } else if (uri == API_GET_SENSOR_STATUS) {
        Poco::Net::HTMLForm form(request, request.stream());
        int32_t person_id = std::atoi(form.get("person_id", "0").c_str());
        std::vector<TuogunProductLine> vec_productLine = TuogunProductLineManager::getTuogunProductLineByPersId(person_id);
        json.clear();
        int normalCount = 0;
        int stopCount = 0;
        int slowCount = 0;
        int otherCount = 0;
        for (auto product_line : vec_productLine) {
            int normalCountTmp = 0;
            int stopCountTmp = 0;
            int slowCountTmp = 0;
            int otherCountTmp = 0;
            getStatusTotals(product_line.id, normalCountTmp, stopCountTmp, slowCountTmp, otherCountTmp);
            normalCount += normalCountTmp;
            stopCount += stopCountTmp;
            slowCount += slowCountTmp;
            otherCount += otherCountTmp;
        }
        nlohmann::json jsonSensorStatus;
        jsonSensorStatus["normalCount"] = normalCount;
        jsonSensorStatus["stopCount"] = stopCount;
        jsonSensorStatus["slowCount"] = slowCount;
        jsonSensorStatus["otherCount"] = otherCount;
        json["code"] = 0;
        json["sensor_status"] = jsonSensorStatus;
        json["message"] = "successful";
        LOGD << "processRequestGet:" << json.dump();
    } else if (uri == API_GET_THRESHOLD) {
        json.clear();
        json["code"] = 0;
        json["threshold"] = common::getThreshold();
        json["message"] = "successful";
        LOGD << "processRequestGet:" << json.dump();
    } else {
        json["code"] = 1;
        json["message"] = "no api";
    }
    ostr << json.dump();
}

void RootHandler::processRequestPost(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response)
{
    std::string uri = request.getURI();
    std::string content_type = request.getContentType();
    uri = uri.substr(0, uri.find('?'));
    content_type = content_type.substr(0, content_type.find(';'));

    std::ostream& ostr = response.send();
    response.setStatus(Poco::Net::HTTPServerResponse::HTTPStatus::HTTP_OK);

    //pplication/x-www-form-urlencoded
    //text/plain
    //application/json
    //multipart/form-data
    nlohmann::json json;
    if (uri == API_USER_LOGIN && content_type == "application/json") {
        response.setContentType("application/json");
        json = nlohmann::json::parse(request.stream());

        std::string username = json["username"];
        std::string password = json["password"];
        Person person = PersonManager().getPersonByPersName(username);

        json.clear();
        if (person.name != username) {
            json["code"] = 1;
            json["message"] = "username or password is error";
        } else {
            std::string pwd = md5Encoder(password);
            // string 转大写
            transform(pwd.begin(), pwd.end(), pwd.begin(), ::toupper);
            if (person.pwd != pwd) {
                json["code"] = 1;
                json["message"] = "username or password is error";
            } else {
                json["code"] = 0;
                json["person_id"] = person.id;
                json["message"] = "login success";
            }
        }
    } else if (uri == API_SET_THRESHOLD) {
        response.setContentType("application/json");
        json = nlohmann::json::parse(request.stream());
        int threshold = json["threshold"];
        common::setThreshold(threshold);

        json.clear();
        json["code"] = 0;
        json["message"] = "set threshold success";
    } else {
        json["code"] = 1;
        json["message"] = "no api";
    }
    ostr <<  json.dump();
}

void RootHandler::processRequestPut(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response)
{
    std::string uri = request.getURI();
    std::string content_type = request.getContentType();
    uri = uri.substr(0, uri.find('?'));
    content_type = content_type.substr(0, content_type.find(';'));

    std::ostream& ostr = response.send();
    response.setStatus(Poco::Net::HTTPServerResponse::HTTPStatus::HTTP_OK);

    if (uri == API_USER_LOGIN) {
    } else if (uri == "/yang/get_user_list") {
    } else {
    }
}
